﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;

namespace ESoft.Core.Util.Extensions
{
    public static class StringExtensions
    {
        private const string emailPattern =
            @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";

        private static readonly Regex EmailRegex = new Regex(emailPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);

        #region IsNullOrEmpty

        /// <summary>
        /// 扩展：IsNullOrEmpty
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string s)
        {
            return string.IsNullOrEmpty(s);
        }

        /// <summary>
        /// 扩展：IsNullOrWhiteSpace
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static bool IsNullOrWhiteSpace(this string s)
        {
            return string.IsNullOrWhiteSpace(s);
        }

        public static bool IsNotNullOrWhiteSpace(this string source)
        {
            return !string.IsNullOrWhiteSpace(source);
        }

        #endregion

        #region To<T>

        public static TValue To<TValue>(this string value)
        {
            return value.To<TValue>(default(TValue));
        }

        public static TValue To<TValue>(this string value, TValue defaultValue)
        {
            try
            {
                var converter1 = TypeDescriptor.GetConverter(typeof(TValue));
                if (converter1.CanConvertFrom(typeof(string))) return (TValue) converter1.ConvertFrom((object) value);
                var converter2 = TypeDescriptor.GetConverter(typeof(string));
                if (converter2.CanConvertTo(typeof(TValue))) return (TValue) converter2.ConvertTo((object) value, typeof(TValue));
            }
            catch
            {
            }

            return defaultValue;
        }

        /// <summary>
        /// 扩展：ToInt
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static int ToInt(this string s, int defaultValue = 0)
        {
            int theRet;
            return int.TryParse(s, out theRet) ? theRet : defaultValue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="s"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static decimal ToDecimal(this string s, decimal defaultValue = 0)
        {
            decimal theRet;
            return decimal.TryParse(s, out theRet) ? theRet : defaultValue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="s"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static double ToDouble(this string s, double defaultValue = 0)
        {
            double theRet;
            return double.TryParse(s, out theRet) ? theRet : defaultValue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="s"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(this string s, DateTime defaultValue)
        {
            DateTime theRet;
            return DateTime.TryParse(s, out theRet) ? theRet : defaultValue;
        }

        public static bool ToBool(this string s, bool defaultValue = false)
        {
            bool theRet;
            return bool.TryParse(s, out theRet) ? theRet : defaultValue;
        }

        public static float ToFloat(this string s, float defaultValue = 0)
        {
            float theRet;
            return float.TryParse(s, out theRet) ? theRet : defaultValue;
        }

        #endregion


        #region Is<T>

        public static bool Is<TValue>(this string value)
        {
            var converter = TypeDescriptor.GetConverter(typeof(TValue));
            if (converter.IsNull().IsFalse())
                try
                {
                    if (value.IsNull().IsFalse())
                        if (!converter.CanConvertFrom((ITypeDescriptorContext) null, value.GetType()))
                            return false;
                    converter.ConvertFrom((ITypeDescriptorContext) null, CultureInfo.CurrentCulture, (object) value);
                    return true;
                }
                catch
                {
                }

            return false;
        }

        public static bool IsInt(this string s)
        {
            int theRet;
            return int.TryParse(s, out theRet);
        }

        public static bool IsDecimal(this string s)
        {
            decimal theRet;
            return decimal.TryParse(s, out theRet);
        }

        public static bool IsDouble(this string s)
        {
            double theRet;
            return double.TryParse(s, out theRet);
        }

        public static bool IsDateTime(this string s)
        {
            DateTime theRet;
            return DateTime.TryParse(s, out theRet);
        }

        public static bool IsBool(this string s)
        {
            bool theRet;
            return bool.TryParse(s, out theRet);
        }

        public static bool IsFloat(this string value)
        {
            float result;
            return float.TryParse(value, out result);
        }

        #endregion

        /// <summary>
        /// string.Format扩展
        /// </summary>
        /// <param name="s"></param>
        /// <param name="objs"></param>
        /// <returns></returns>
        public static string FormatWith(this string s, params object[] objs)
        {
            return string.Format(s, objs);
        }

        /// <summary>
        /// 字符串重复输出多次
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="num">The number.</param>
        /// <returns>System.String.</returns>
        public static string Replicate(this string str, int num)
        {
            var result = new StringBuilder(str);
            for (var i = 1; i < num; i++) result.Append(str);
            return result.ToString();
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T JsonTo<T>(this string value)
        {
            var entity = JsonConvert.DeserializeObject<T>(value);
            return entity;
        }

        /// <summary>
        /// 字符串截取,超过则用...表示
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="maxLength">The maximum length.</param>
        /// <returns>System.String.</returns>
        public static string TruncateWithPostfix(this string str, int maxLength)
        {
            return TruncateWithPostfix(str, maxLength, "...");
        }

        /// <summary>
        /// 字符串按指定长度截取，超过则用postfix表示
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="maxLength">The maximum length.</param>
        /// <param name="postfix">The postfix.</param>
        /// <returns>System.String.</returns>
        public static string TruncateWithPostfix(this string str, int maxLength, string postfix)
        {
            if (str == null)
                return null;

            if (str == string.Empty || maxLength == 0)
                return string.Empty;

            if (str.Length <= maxLength)
                return str;

            if (maxLength <= postfix.Length)
                return postfix.Left(maxLength);

            return str.Left(maxLength - postfix.Length) + postfix;
        }

        /// <summary>
        /// 取字符串左边len位
        /// </summary>
        /// <param name="str">The string.</param>
        /// <param name="len">The length.</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.ArgumentNullException">str</exception>
        /// <exception cref="System.ArgumentException">len argument can not be bigger than given string's length!</exception>
        public static string Left(this string str, int len)
        {
            if (str == null)
                throw new ArgumentNullException(nameof(str));

            if (str.Length < len)
                throw new ArgumentException("len argument can not be bigger than given string's length!");

            return str.Substring(0, len);
        }

        /// <summary>
        /// 字符串通过分割符转成对应T数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="this">The this.</param>
        /// <param name="splitCharacters">The split characters.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>T[].</returns>
        /// <exception cref="System.ArgumentNullException">this</exception>
        public static T[] SplitToArray<T>(this string @this, char[] splitCharacters, T defaultValue = default(T))
        {
            if (string.IsNullOrWhiteSpace(@this)) throw new ArgumentNullException(nameof(@this));
            return @this.Split(splitCharacters, StringSplitOptions.RemoveEmptyEntries).Select(m => m.To<T>(defaultValue)).ToArray();
        }

        /// <summary>
        /// 字符串通过分割符转成对应T数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="this">The this.</param>
        /// <param name="splitStrings">The splict string.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>T[].</returns>
        /// <exception cref="System.ArgumentNullException">this</exception>
        public static T[] SplitToArray<T>(this string @this, string[] splitStrings, T defaultValue = default(T))
        {
            if (string.IsNullOrWhiteSpace(@this)) throw new ArgumentNullException(nameof(@this));
            return @this.Split(splitStrings, StringSplitOptions.RemoveEmptyEntries).Select(m => m.To<T>(defaultValue)).ToArray();
        }

        /// <summary>
        /// 是否为邮箱
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns><c>true</c> if the specified source is email; otherwise, <c>false</c>.</returns>
        public static bool IsEmail(this string source)
        {
            if (source.IsNull()) return false;
            return EmailRegex.IsMatch(source);
        }

        public static string ThrowIfNullOrEmpty(this string @this, string throwMessaage = "不能为空")
        {
            if (@this.IsNullOrEmpty()) throw new Exception(throwMessaage);
            return @this;
        }
    }
}